//============================================================================
// Name        : barnesOpenCL.cpp
// Author      : Antonio.Vilches
// Version     : 1.0
// Copyright   :
// Description : BarnesHut application on OpenCL platform
//============================================================================

#include <iostream>
#include <OpenCL/opencl.h>
using namespace std;


	//programa principal
	int main(int argc, char *argv[]) {

		fprintf(stderr, "\n Simulacion barnesHut Openmp\n");
		fprintf(stderr, "Version Openmp: static [numero_iteraciones/hebras]\n");

		/*if (argc != 3) {
			fprintf(stderr, "argumentos: fichero_entrada numero_de_threads \n");
			exit(-1);
		}*/
		/*Setting Up OpenCL*/
		int err;
		int gpu=1;
		cl_context context;
		cl_command_queue queue;
		cl_program program;
		cl_kernel kernel;
		cl_device_id device_id;


		err = clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU,1,&device_id,NULL);
		/*if there isn't any error openCL will work*/
		if(err!=CL_SUCCESS){
			printf("Error at getDeviceIds funtion\n");
			exit(-1);
		}
		context=clCreateContext(0,1,&device_id,NULL,NULL, &err);
		if(!context){
			printf("Error at clCreateContext funtion\n");
			exit(-1);
		}

		queue = clCreateCommandQueue(context, device_id,0,&err);
		if(!queue){
			printf("Error at clCreateCommandQueue funtion\n");
			exit(-1);
		}

		/*body = NULL;
		nthreads = atoi(argv[2]);


		omp_set_num_threads(nthreads);
		fprintf(stderr, "Ejecutado con el fichero de entrada: %s y con threads: %d \n",argv[1],nthreads);

		timeval starttime, endtime,startgrow, endgrow, startadvance, endadvance, startcompute,endcompute,startfile,endfile;
		register double runtime=0,runtimegrow=0,runtimeadvance=0,runtimecompute=0,runtimefile=0;


			gettimeofday(&startfile, NULL);
			ReadInput(argv[1]);
			gettimeofday(&endfile, NULL);
			runtimefile +=  (endfile.tv_sec * 1000.0 + endfile.tv_usec / 1000.0 - startfile.tv_sec * 1000.0 				- 			startfile.tv_usec / 1000.0);


			gettimeofday(&starttime, NULL);

			for (step = 0; step < timesteps; step++) { // time-step the system

				gettimeofday(&startgrow, NULL);
				register double diameter, centerx, centery, centerz;
				ComputeCenterAndDiameter(nbodies, diameter, centerx, centery, centerz);

				OctTreeInternalNode *root = OctTreeInternalNode::NewNode(centerx, centery, centerz); // create the tree's root

				const double radius = diameter * 0.5;
				for (int i = 0; i < nbodies; i++) {
					root->Insert(body[i], radius); // grow the tree by inserting each body
				}

				register int curr = 0;
				root->ComputeCenterOfMass(curr); // summarize subtree info in each internal node (plus restructure tree and sort bodies for performance reasons)
				gettimeofday(&endgrow, NULL);
				runtimegrow +=  (endgrow.tv_sec * 1000.0 + endgrow.tv_usec / 1000.0 - startgrow.tv_sec * 1000.0 				- startgrow.tv_usec / 1000.0);

				groot = root;
				gdiameter = diameter;

				gettimeofday(&startcompute, NULL);
				#pragma omp parallel for schedule(static)
				for (int i = 0; i < nbodies; i++) {
					//Process(i);
					body[i]->ComputeForce(groot, gdiameter);
				}
				gettimeofday(&endcompute, NULL);
				runtimecompute += (endcompute.tv_sec * 1000.0 + endcompute.tv_usec / 1000.0 - startcompute.tv_sec * 1000.0 				- startcompute.tv_usec / 1000.0);

				OctTreeInternalNode::RecycleTree(); // recycle the tree

				gettimeofday(&startadvance, NULL);
				for (int i = 0; i < nbodies; i++) { // the iterations are independent: they can be executed in any order and in parallel
					body[i]->Advance(); // advance the position and velocity of each body
				}
				gettimeofday(&endadvance, NULL);
				runtimeadvance += (endadvance.tv_sec * 1000.0 + endadvance.tv_usec / 1000.0 - startadvance.tv_sec * 1000.0 				- startadvance.tv_usec / 1000.0);
			} // end of time step

			gettimeofday(&endtime, NULL);
			runtime = (endtime.tv_sec*1000.0 + endtime.tv_usec/1000.0 - starttime.tv_sec*1000.0 - starttime.tv_usec / 1000.0);

			gettimeofday(&startfile, NULL);



		for (int i = 0; i < nbodies; i++) { // print result
			PrintDouble(body[i]->posx);
			printf(" ");
			PrintDouble(body[i]->posy);
			printf(" ");
			PrintDouble(body[i]->posz);
			printf("\n");
		}
		gettimeofday(&endfile, NULL);
			runtimefile +=  (endfile.tv_sec * 1000.0 + endfile.tv_usec / 1000.0 - startfile.tv_sec * 1000.0 				- 			startfile.tv_usec / 1000.0);

		fprintf(stderr, "runtime: %lf ms\n\n", runtime);
		fprintf(stderr, "runtime_readfile: %lf ms\n", runtimefile);
		fprintf(stderr, "runtime_grow: %lf ms\n", runtimegrow);
		fprintf(stderr, "runtime_compute: %lf ms\n", runtimecompute);
		fprintf(stderr, "runtime_advance: %lf ms\n", runtimeadvance);
		 */
		return 0;
}

